本文主要內容為探討基本型別的特殊值並能適當地使用它們。
void 運算子可確保運算式不回傳任何值(其實是得到 undefined),並且不修改現有值。
例如,這個有一個變數 hello,其值為 777,結合 void 運算子做運算後會得到 undefined,但 hello 內儲存的值仍是不變的,依舊是 777。
var hello = 777;
void hello // undefined
hello // 777
實際上會應用到什麼狀況呢?
一,運算式的結果真的希望 不回傳任何值(再次強調,其實是回傳 undefined),除了直接寫「undefined」外,還可以用「void 某個值」,通常會用「void 0」。
function sayHi() {
return void 0;
}
const result = sayHi()
result // undefined
二,在程式設定下,必須區別有意義和無意義的回傳值,而無意義的回傳值希望能是 undefined,以避免後續誤判為「有意義」的回傳值而做了錯誤的操作。如下範例所示,這裡有一個定期檢查回傳結果的函式 check,check 會呼叫函式 getResult 來得到運算結果並確認結果為何,若沒有得到結果,就顯示經過的分鐘數;若得到結果就印出「工作完成」的訊息。在這裡無意義的回傳值是使用 undefined,但當然很多開發者是比較喜歡用 false 或 null,就看當時的需求和個人喜好摟。
const interval = 60000;
let start = null;
let counter = 1;
// 經由一些運算得到結果 result,若有結果則 flag "isDone" 設為 true 並回傳結果;若無結果則 flag "isDone" 設為 false 並回傳 undefined
function getResult() {
if (isDone) {
return result;
}
return void 0; // 等同於 undefined
}
// 不斷重複詢問是否得到結果?若沒有得到結果,就顯示經過的分鐘數;若得到結果就印出「工作完成」的訊息
function check(timestamp) {
const progress = timestamp - start;
if (start === null) { start = timestamp; }
if (progress < interval) {
requestAnimationFrame(check);
} else {
if (getResult()) {
console.log('工作完成!');
} else {
console.log(`checking...time passed: ${counter} minute(s).`);
counter++;
start = timestamp;
requestAnimationFrame(check);
}
}
}
requestAnimationFrame(check);
點此看完整範例。
NaN 表示值為無效的數字(invalid number),會產生 NaN 的原因是
0 / 0
、Infinity / Infinity
都會得到 NaN就會產生 NaN。
NaN 有幾個有趣的議題...以下分別討論之。
NaN 既然表示是無效的數字,依舊還是數字,因此在資料型別的檢測 typeof NaN
結果就是 number,不要被字面上的意思「不是數字」(not a number)給弄糊塗了。
NaN 與任何數字運算都會得到 NaN。
NaN 不大於、不小於也不等於任何值,包含 NaN 它自己。
isNaN
與 Number.isNaN
要如何檢測運算結果是否為有效的數字呢?那麼就來檢測是否為無效的數字-NaN 就可以了。在 ES6 以前,開發者使用 isNaN
在數學運算或解析字串後檢測得到的結果是否為合法的數字,其實就是檢測是否為 NaN,其過程為先將輸入值使用 Number 強制轉型為數字,若無法轉為有效的數字而得到 NaN 時就判定等於 NaN,結果得到 true。
範例如下,空物件 {}
經過 isNaN 判斷是 NaN,意即為無效的數字。
isNaN({}) // true,此為無效的數字
// 拆解詳細過程如下...
Number({}) // 先將空物件轉為數字,得到 NaN
isNaN(NaN) // 檢查是否為 NaN,得到 true
其他範例還有...
isNaN(123) // false
isNaN(-1.23) // false
isNaN(5-2) // false
isNaN(0) // false
isNaN('123') // false
isNaN('Hello World') // true
isNaN('2000/01/01') // true
isNaN('') // false
isNaN(true) // false
isNaN(undefined) // true
isNaN('NaN') // true
isNaN(NaN) // true
isNaN(0/0) // true
isNaN(1/0) // false
但這檢測方式的常常會讓開發者得到讓人容易誤解的結果(像是...大多數的人都會爭論...空物件 {}
就真的不等於 NaN 呀),因此 ES6 推出了 Number.isNaN
,Number.isNaN
不會經過轉為數字的這個過程,而是直接判斷型別是否為數字且是否等於 NaN。承上範例,使用 Number.isNaN
檢測空物件 {}
是否為 NaN,得到 false。
Number.isNaN({}) // 直接檢查空物件是否為 NaN,得到 false
同樣也來看剛才的範例...
Number.isNaN(123) // false
Number.isNaN(-1.23) // false
Number.isNaN(5-2) // false
Number.isNaN(0) // false
Number.isNaN('123') // false
Number.isNaN('Hello World') // false
Number.isNaN('2000/01/01') // false
Number.isNaN('') // false
Number.isNaN(true) // false
Number.isNaN(undefined) // false
Number.isNaN('NaN') // false
Number.isNaN(NaN) // true
Number.isNaN(0/0) // true
Number.isNaN(1/0) // false
雖然 ES6 出了這個新功能,但不見得所有的瀏覽器都會支援,因此對於較舊的瀏覽器,就掛個 polyfill 來模擬這個新功能。
isNaN(NaN); // true
isNaN(Number.NaN); // true
polyfill 如下。
if (!Number.isNaN) {
Number.isNaN = function isNaN(x) {
return x !== x; // NaN 是唯一一個不等於自己的值
};
}
...
...
面對這種瀏覽器我都很想送它一張恐龍圖,祝它早日被社會淘汰。
無限分為正無限(在 ES6 定義為 Number.POSITIVE_INFINITY
)和負無限(在 ES6 定義為 Number.NEGATIVE_INFINITY
),在數學運算中會得到無限的原因是
1 / 0
得到 Infinity,-1 / 0
得到 -InfinityNumber.MAX_VALUE + Math.pow(2, 970)
得到 Infinity (備註)又,無限與無限做數學運算,一般來說會得到無限。除了...
0 / 0
、Infinity / Infinity
都會得到 NaN1 / Infinity
得到 0,-1 / Infinity
得到 -0備註:若運算結果接近 Number.MAX_VALUE
而非 Infinity,則會取一個最接近的值為 Number.MAX_VALUE
,這稱為「向下約整」(rounds down);同理,若運算結果接近 Infinity 而非 Number.MAX_VALUE
,則會取一個最接近的值為 Infinity,這稱為「向上約整」(rounds up)。
零分為正零(+0)和負零(-0),正負號在表達方向上是很有用的。其中,產生負零的原因是乘除運算中,運算元的其中一方為負數,例如:-0 / 1
或 0 / -1
會得到 -0。
零有幾個有趣的議題...以下分別討論之。
不管是正零(+0)或負零(-0),轉字串後一律為「'0'
」。
(+0).toString() // "0"
String(+0) // "0"
'' + (+0) // "0"
(-0).toString() // "0"
String(-0) // "0"
'' + (-0) // "0"
相反的,若從字串轉數字,則
'+0'
)會轉成數字 0 或報錯,但其實正零一般來說都是表示為「0」。'-0'
)會轉成數字 -0+'+0' // 0
Number('+0') // 0
JSON.parse('+0') // Uncaught SyntaxError: Unexpected token + in JSON at position 0
+'-0' // -0
Number('-0') // -0
JSON.parse('-0') // -0
正零(+0)或負零(-0)是無法從比較運算子和相等運算子中得到差異。
var a = 0; // 0
var b = 0 / -1; // -0
a == b; // true
-0 == 0; // true
a === b; // true
-0 === 0; // true
0 > -0; // false
a > b; // false
那到底要如何辨別正零和負零呢?
解法的步驟如下
Number(-0)
為 -0,並檢查結果是否等於零1 / -0
得到 -Infinity,因此就可檢測輸入值是否為負零function isNegZero(n) {
n = Number(n);
return (n === 0) && (1 / n === -Infinity);
}
// 測試...
isNegZero(-0); // true
isNegZero(0/-1); // true
isNegZero(0); // false
稍後會再提供另一個解法-Object.is(..)
。
針對負零(-0)和 NaN 的比較,除了以上提過的方法外,還可以用 Object.is(..)
來做檢測。
Object.is(..)
會比較兩值是否相等,而 Object.is(..)
的運作和嚴格相等是一樣的,但會將 NaN、-0 和 +0 獨立處理。到底怎麼定義「相等」呢?有興趣的可以參考 MDN 的說明。
var a = Number('Hello World'); // NaN
var b = 0 / -1; // -0
Object.is(a, NaN); // true
Object.is(b, -0); // true
Object.is(b, 0); // false
...
...
世紀難題都被它解決了!讚讚!
看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到...
typeof NaN
為 number;(2) NaN 與任何數字運算都會得到 NaN;(3) NaN 不大於、不小於也不等於任何值,包含 NaN 它自己;(4) 可使用 Number.isNaN
檢測值是否等於 NaN。Object.is(..)
來做偵測。同步發表於部落格。